/*
Copyright 2007 John Tsiombikas <nuclear@siggraph.org>

This file is part of the pixelshow 2007 invitation demo.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <GL/gl.h>
#include <kdtree.h>
#include "mesh.h"

struct mesh *create_mesh(struct vertex *varr, int vnum)
{
	struct mesh *m;

	if(!(m = malloc(sizeof *m))) {
		return 0;
	}
	m->varr = 0;
	m->vnum = vnum;
	m->kdtree = kd_create();

	if(vnum) {
		if(!(m->varr = malloc(vnum * sizeof *m->varr))) {
			free(m);
			return 0;
		}

		if(varr) {
			memcpy(m->varr, varr, vnum * sizeof *m->varr);
			make_kdtree(m);
		}
	}

	return m;
}

void free_mesh(struct mesh *m)
{
	free(m->varr);
	free(m);
}

void make_kdtree(struct mesh *m)
{
	int i;

	kd_clear(m->kdtree);
	for(i=0; i<m->vnum; i++) {
		kd_insert(m->kdtree, m->varr[i].pos.x, m->varr[i].pos.y, m->varr[i].pos.z, &m->varr[i]);
	}
}

static vec3_t v3_cross(vec3_t v1, vec3_t v2)
{
	vec3_t v;
	v.x = v1.y * v2.z - v1.z * v2.y;
	v.y = v1.z * v2.x - v1.x * v2.z;
	v.z = v1.x * v2.y - v1.y * v2.x;
	return v;
}

#define MAX_ADJ		64
void calc_normals(struct mesh *mesh)
{
	int i, j, k, tcount;
	int **adj_tri;
	int *adj_tri_num;
	vec3_t *tri_norm;

	tcount = mesh->vnum / 3;
	tri_norm = malloc(tcount * sizeof *tri_norm);

	adj_tri = malloc(mesh->vnum * sizeof *adj_tri);
	adj_tri_num = malloc(mesh->vnum * sizeof *adj_tri_num);

	/* calculate triangle normals */
	for(i=0; i<tcount; i++) {
		vec3_t v[3], a, b;
		v[0] = mesh->varr[i * 3].pos;
		v[1] = mesh->varr[i * 3 + 1].pos;
		v[2] = mesh->varr[i * 3 + 2].pos;

		a.x = v[1].x - v[0].x; a.y = v[1].y - v[0].y; a.z = v[1].z - v[0].z;
		b.x = v[2].x - v[0].x; b.y = v[2].y - v[0].y; b.z = v[2].z - v[0].z;
		tri_norm[i] = v3_cross(a, b);

		/* for every vertex of the triangle */
		for(j=0; j<3; j++) {
			int vidx = i * 3 + j;

			/* find its neighboring vertices */
			void *rset = kd_nearest_range(mesh->kdtree, v[j].x, v[j].y, v[j].z, 0.0001);
			int res_size = kd_res_size(rset);

			adj_tri_num[vidx] = res_size;
			adj_tri[vidx] = malloc(res_size * sizeof **adj_tri);

			for(k=0; k<res_size; k++) {
				adj_tri[vidx][k] = ((struct vertex*)kd_res_item_data(rset) - mesh->varr) / 3;
				kd_res_next(rset);
			}
			kd_res_free(rset);
		}
	}

	for(i=0; i<mesh->vnum; i++) {
		float len;
		vec3_t norm = {0, 0, 0};
		for(j=0; j<adj_tri_num[i]; j++) {
			vec3_t tn = tri_norm[adj_tri[i][j]];
			norm.x += tn.x;
			norm.y += tn.y;
			norm.z += tn.z;
		}

		len = adj_tri_num[i] > 0 ? sqrt(norm.x * norm.x + norm.y * norm.y + norm.z * norm.z) : 1.0;
		mesh->varr[i].norm.x = norm.x / len;
		mesh->varr[i].norm.y = norm.y / len;
		mesh->varr[i].norm.z = norm.z / len;
	}

	for(i=0; i<mesh->vnum; i++) {
		free(adj_tri[i]);
	}
	free(adj_tri);
	free(adj_tri_num);
	free(tri_norm);
}

void draw_mesh(struct mesh *m)
{
	int i;

	glBegin(GL_TRIANGLES);
	for(i=0; i<m->vnum; i++) {
		glNormal3f(m->varr[i].norm.x, m->varr[i].norm.y, m->varr[i].norm.z);
		glTexCoord3f(m->varr[i].texc.x, m->varr[i].texc.y, m->varr[i].texc.z);
		glVertex3f(m->varr[i].pos.x, m->varr[i].pos.y, m->varr[i].pos.z);
	}
	glEnd();
}
